/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "propertyInspectorDelegate.h"
#include <stdio.h>
#include <QComboBox>
#include <QCheckBox>
#include <QSpinBox>
#include <QListView>
#include <QItemSelectionModel>
#include <QStandardItem>
#include <QDialog>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDesktopWidget>
#include <QApplication>
#include "assert.h"

#include <string>
#include <sstream>
#include "property.h"
using namespace std;

map<string, QAbstractItemModel *> listModels;

MultiSelection:: MultiSelection(QWidget *parent): QDialog(parent, Qt::Sheet) {
  setMinimumSize(400,400);
  list = new QListView(this);
  list->setAutoScroll(true);
  list->setSelectionMode(QAbstractItemView::MultiSelection);
  QRect scr= QApplication::desktop()->screenGeometry();
  //move(scr.width()/2, scr.height()/2);
  
  QVBoxLayout *layout = new QVBoxLayout();
  QPushButton *button = new QPushButton("OK");
  connect(button, SIGNAL(clicked()), SLOT(closeMe()));
  
  layout->addWidget(list);
  layout->addWidget(button);
  setLayout(layout);  
  selection = "";
  model = 0;
}

void MultiSelection::setPropertyIndex(const QModelIndex &idx) {
  propertyIndex = idx;
}

void MultiSelection::setData(string data) {
  assert(model);
  istringstream iss(data);
  string token;

  QItemSelectionModel *selectionModel = list->selectionModel(); //new QItemSelectionModel(model); //list->selectionModel();
  assert(selectionModel);
  selectionModel->clear();
  while(getline(iss, token, ':')) {
    int size = model->rowCount();
    for(int i=0;i<size;i++) {
      QModelIndex index = model->index(i,0);
      string name = model->data(index,Qt::EditRole).toString().toStdString();
      if(name==token) {
				selectionModel->select(index, QItemSelectionModel::Select);
      }
    }
  }
}

void MultiSelection::setModel(QAbstractItemModel *model) {
  this->model = model;
  list->setModel(model);
}
void MultiSelection::closeMe() {
  QItemSelectionModel *selectionModel = list->selectionModel();
  QModelIndexList list = selectionModel->selectedIndexes();
  selection="";
  for(int i=0;i<list.size();i++) { 
    string name = model->data(list[i],Qt::EditRole).toString().toStdString();
    selection=selection+string(":")+name;
  }
  QAbstractItemModel *model =  (QAbstractItemModel*) propertyIndex.model();
  model->setData(propertyIndex,QString(selection.c_str()), Qt::EditRole);
  this->hide();
}


PropertyInspectorDelegate::PropertyInspectorDelegate(QObject *parent)
     : QItemDelegate(parent)
 {
   //      printf("item delegate create\n");
 }

void PropertyInspectorDelegate::addStringModel(string name, QAbstractItemModel *model) {
  listModels[name] = model;
}

QWidget *PropertyInspectorDelegate::createNumericEditor(QWidget *parent,
     const QStyleOptionViewItem & /*option*/,
     const QModelIndex & /*index*/) const
{
  QSpinBox *editor = new QSpinBox(parent);
  editor->setMinimum(0);
  editor->setMaximum(INT_MAX);
  connect(editor, SIGNAL(valueChanged(int)), SLOT(changed(int)));
  return editor;
}
void PropertyInspectorDelegate::setNumericEditorData(QWidget *editor,
                                     const QModelIndex &index) const  
{
  QSpinBox *spin = static_cast<QSpinBox*>(editor);
  int value = (index.data().toInt());
  spin->setValue(value);
}
void PropertyInspectorDelegate::setNumericModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
  QSpinBox *spin = static_cast<QSpinBox*>(editor);
  int value = spin->value();  
  model->setData(index, QString("%0").arg(value), Qt::EditRole);
}


QWidget *PropertyInspectorDelegate::createDoubleEditor(QWidget *parent,
     const QStyleOptionViewItem & /*option*/,
     const QModelIndex & /*index*/, double minval, double maxval) const
{
  QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
  connect(editor, SIGNAL(valueChanged(double)), SLOT(changedDouble(double)));
  editor->setMinimum(minval);
  editor->setMaximum(maxval);
  editor->setSingleStep(0.001);
	editor->setDecimals(4);
  return editor;
}
void PropertyInspectorDelegate::setDoubleEditorData(QWidget *editor,
						    const QModelIndex &index) const  
{
  QDoubleSpinBox *spin = static_cast<QDoubleSpinBox*>(editor);
  double value = (index.data().toDouble());
  spin->setValue(value);
}
void PropertyInspectorDelegate::setDoubleModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
  QDoubleSpinBox *spin = static_cast<QDoubleSpinBox*>(editor);
  double value = spin->value();
  model->setData(index, QString("%0").arg(value), Qt::EditRole);
}

QWidget *PropertyInspectorDelegate::createBoolEditor(QWidget *parent,
     const QStyleOptionViewItem & /*option*/,
     const QModelIndex & /*index*/) const
{
  
  QComboBox *editor = new QComboBox(parent);
  editor->addItem("false");
  editor->addItem("true");
  connect(editor, SIGNAL(currentIndexChanged(int)), SLOT(changed(int)));
  return editor;
  /*
  QCheckBox *editor  = new QCheckBox(parent);
  editor->setAutoFillBackground(true);
  connect(editor, SIGNAL(stateChanged(int)), SLOT(changed(int)));
  return editor;
  */
}
void PropertyInspectorDelegate::setBoolEditorData(QWidget *editor,
                                     const QModelIndex &index) const  
{
  QComboBox *check = static_cast<QComboBox*>(editor);
  QString qstr = (index.data().toString());
  if(qstr.toStdString()=="true") {
    check->setCurrentIndex(1);
  } else {
    check->setCurrentIndex(0);
  }
  /*
  QCheckBox *check =  static_cast<QCheckBox*>(editor);
  QString qstr = (index.data().toString());
  check->setText(qstr);
  check->setChecked(qstr.toStdString()=="true");
  */
}
void PropertyInspectorDelegate::setBoolModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
{
  QComboBox *comboBox = static_cast<QComboBox*>(editor);
  int value = comboBox->currentIndex();
  QString str = QString(value?"true":"false");
  model->setData(index, str, Qt::EditRole);

  /*
  QCheckBox *check =  static_cast<QCheckBox*>(editor);
  bool value = check->isChecked();
  printf("Value = %d\n", value);
  QString str = QString(value?"true":"false");
  model->setData(index, str, Qt::EditRole);
  */
}

QWidget *PropertyInspectorDelegate::createEditor(QWidget *parentWidget,
     const QStyleOptionViewItem & option,
     const QModelIndex & index) const
 {
   if(index.column()==1) {

     QModelIndex typeIdx = index.sibling(index.row(), 2);
     int type = typeIdx.data().toInt();
     string listModel = index.sibling(index.row(),3).data().toString().toStdString();


     if(type==PROPERTY_BOOL) {
       return createBoolEditor(parentWidget, option, index);
     }
     if(type==PROPERTY_INT) {
       return createNumericEditor(parentWidget, option, index);
     }
     if(type==PROPERTY_FLOAT) {
       return createDoubleEditor(parentWidget, option, index);
     }
    if(type==PROPERTY_SIGNED) {
      return createDoubleEditor(parentWidget, option, index, -DBL_MAX, DBL_MAX);
     }
    if(type==PROPERTY_MULTISELECTION) {
       MultiSelection *editor = new MultiSelection(parentWidget);
       if(listModels[listModel]) editor->setModel(listModels[listModel]);
       editor->setData(index.data().toString().toStdString());
       editor->setPropertyIndex(index);
       editor->show();
       return editor;
     }
     if(type==PROPERTY_SELECTION) {
       QComboBox *editor = new QComboBox(parentWidget);
			 editor->setView(new QListView());
       if(listModels[listModel]) editor->setModel(listModels[listModel]);
       connect(editor, SIGNAL(currentIndexChanged(int)), SLOT(changed(int)));
				
       return editor;
     }
     return QItemDelegate::createEditor(parentWidget, option, index);
   } else {
     return QItemDelegate::createEditor(parentWidget, option, index);
   }
     
 }

void PropertyInspectorDelegate::changed(int) {
  QWidget *editor = qobject_cast<QWidget *>(sender());
  commitData(editor);
}

void PropertyInspectorDelegate::changedDouble(double) {
  QWidget *editor = qobject_cast<QWidget *>(sender());
  commitData(editor);
}

void PropertyInspectorDelegate::setEditorData(QWidget *editor,
                                     const QModelIndex &index) const
 {
    if(index.column()==1) {
      //      printf("setEditorData for column %d value :", index.column());

      QModelIndex typeIdx = index.sibling(index.row(), 2);
      int type = typeIdx.data().toInt();
      string listModel = index.sibling(index.row(),3).data().toString().toStdString();
      if(type==PROPERTY_BOOL) {
	setBoolEditorData(editor, index);
	return;
      }
      if(type==PROPERTY_INT) {
	setNumericEditorData(editor, index);
	return;
      }
      if(type==PROPERTY_FLOAT || type== PROPERTY_SIGNED) {
	setDoubleEditorData(editor, index);
	return;
      }
       if(type==PROPERTY_SELECTION) {
				 // For a QStringListModel
				 /**
					QStringListModel *model = static_cast<QStringListModel*>(listModels[listModel]);
					if(!model) return;
					
					QStringList list = model->stringList();
					QString qstr = (index.data().toString());
					int index = list.indexOf(qstr);
					QComboBox *combo = static_cast<QComboBox*>(editor);
					combo->setCurrentIndex(index);
					**/
				 // For a QStandardItemModel
				 QString qstr = (index.data().toString());
				 QStandardItemModel *model = static_cast<QStandardItemModel*>(listModels[listModel]);
				 QList<QStandardItem*> list = model->findItems(qstr);
				 if(list.size()>0) {
					 QStandardItem*item = list.first();
					 QComboBox *combo = static_cast<QComboBox*>(editor);
					 combo->setCurrentIndex(item->index().row());
				 }
				 
				//int idx = index.row();
				//QStandardItem *item = model->itemFromIndex(index);
				//string name = item->data(Qt::EditRole).toString().toStdString();
				//string item = model->data(index, Qt::EditRole).toString().toStdString();
				return;
			 }
			 if(type==PROPERTY_MULTISELECTION) {
					return;
       }
      QItemDelegate::setEditorData(editor, index);
    } else {
      QItemDelegate::setEditorData(editor, index);
    }

 }

void PropertyInspectorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                    const QModelIndex &index) const
 {
		if(index.column()==1) {
		 QModelIndex typeIdx = index.sibling(index.row(), 2);
		 int type = typeIdx.data().toInt();
		 
		 if(type==PROPERTY_BOOL) {
			 setBoolModelData(editor, model, index);
			 return;
		 }
		 if(type==PROPERTY_INT) {
			 setNumericModelData(editor, model, index);
			 return;
		 }
		 if(type==PROPERTY_FLOAT || type==PROPERTY_SIGNED) {
			 setDoubleModelData(editor, model, index);
			 return;
		 }
		if(type==PROPERTY_SELECTION) {
			 string listModel = index.sibling(index.row(),3).data().toString().toStdString();
			 QComboBox *comboBox = static_cast<QComboBox*>(editor);
			 int value = comboBox->currentIndex();
			 if(!listModels[listModel]) return;
			
				string typeProp = listModels[listModel]->data(listModels[listModel]->index(value, 1), Qt::EditRole).toString().toStdString();
				model->setData(index, listModels[listModel]->data(listModels[listModel]->index(value, 1), Qt::EditRole), Qt::EditRole);
			 return;
		}
    if(type==PROPERTY_MULTISELECTION) {
      return;
    }
    QItemDelegate::setModelData(editor, model, index);
   } else {
     QItemDelegate::setModelData(editor, model, index);
   }
 }


void PropertyInspectorDelegate::updateEditorGeometry(QWidget *editor,
     const QStyleOptionViewItem &option, const QModelIndex & index ) const
 {
   if(index.column()==1) {
     editor->setGeometry(option.rect);
   } else {
     QItemDelegate::updateEditorGeometry(editor, option, index);
   }
 }
